import cv2
import os
import win32gui
import threading
import time
from playsound import playsound
from PySide6.QtCore import Qt
from PySide6.QtGui import QValidator, QImage, QPixmap
from PySide6.QtWidgets import  QWidget, QHBoxLayout, QVBoxLayout
from qfluentwidgets import SingleDirectionScrollArea, SimpleCardWidget, PushButton, LineEdit, ComboBox, CaptionLabel
import setting as c
import core.model_signin as u
from core.screenshot import hwnd_title, get_all_hwnd, get_screenshot_image, get_mss_screenshot_image
from core.log import logger
from core.util import time_str, delete_file, loadini_subscribe_kw2sound
from core.maocr import maocr


default_sound = [
    'kw1.mp3',
    'kw2.mp3',
    'kw3.mp3',
]


def background_task(obj):
    while True:
        if obj.flag_start:
            logger.info(f"订阅监测任务 {obj.flag_start}")
        time.sleep(c.SUBSCRIBE_INTERVAL)
        if obj.flag_start:
            obj.detect()
    
class LengthValidator(QValidator):
    def __init__(self, max_length=5, parent=None):
        super(LengthValidator, self).__init__(parent)
        self.max_length = max_length

    def validate(self, s, pos):
        # 计算字符串长度，对于UTF-8编码，一个汉字长度为1
        if len(s) <= self.max_length:
            return QValidator.Acceptable, s, pos
        else:
            return QValidator.Invalid, s, pos

    def fixup(self, s):
        # 超出长度时的处理方式，这里简单截断到最大长度
        return s[:self.max_length]

class SubscribeInfoCard(SimpleCardWidget):
    """ App information card """

    def __init__(self, parent=None):
        super().__init__(parent)
        
        self.kw2sound = dict()
        self.load_subscribe_kw2sound()
        
        self.flag_start = False
        self.str_leftcount = ""
        self.kw = ["" for _ in range(3)]
        # 创建线程
        self.background_thread = threading.Thread(target=background_task, args=(self,), daemon=True)
        self.background_thread.start()
       
        self.alert_lb = CaptionLabel()
        self.alert_lb.setText(c.TIPS_SUBSCRIBE_ALERT)
        self.alert_lb.setOpenExternalLinks(True)
        
        self.win_combo = ComboBox()
        self.update_win_combo()
        self.win_label = CaptionLabel('监测窗口: ')
        
        validator = LengthValidator(5)
        
        self.kw1_label = CaptionLabel('订阅关键词1: ')
        self.kw1_le = LineEdit()
        self.kw1_le.setValidator(validator)
        self.kw1_le.setPlaceholderText("优先级：高")
        
        self.kw2_label = CaptionLabel('订阅关键词2: ')
        self.kw2_le = LineEdit()
        self.kw2_le.setValidator(validator)
        self.kw2_le.setPlaceholderText("优先级：中")
        
        self.kw3_label = CaptionLabel('订阅关键词3: ')
        self.kw3_le = LineEdit()
        self.kw3_le.setValidator(validator)
        self.kw3_le.setPlaceholderText("优先级：低")
        
        self.tips_label = CaptionLabel('')
        self.switch_btn = PushButton('开始订阅监测')
        self.switch_btn.clicked.connect(self.onclick_switch)
        
        self.chat_label = CaptionLabel()

        self.vBoxLayout = QVBoxLayout(self)
        self.vBoxLayout_alert= QVBoxLayout()
        self.hBoxLayout_combo= QHBoxLayout()
        self.hBoxLayout_kw1 = QHBoxLayout()
        self.hBoxLayout_kw2 = QHBoxLayout()
        self.hBoxLayout_kw3 = QHBoxLayout()
        self.vBoxLayout_switch = QVBoxLayout()
        self.vBoxLayout_chat = QVBoxLayout()
        
        self.initLayout()

    def initLayout(self):
        self.vBoxLayout_alert.setSpacing(30)
        self.vBoxLayout_alert.setContentsMargins(34, 2, 24, 2)
        self.vBoxLayout_alert.addWidget(self.alert_lb)
        
        self.hBoxLayout_combo.setSpacing(30)
        self.hBoxLayout_combo.setContentsMargins(34, 2, 24, 2)
        self.hBoxLayout_combo.addWidget(self.win_label, 1)
        self.hBoxLayout_combo.addWidget(self.win_combo, 4)
        
        self.hBoxLayout_kw1.setSpacing(30)
        self.hBoxLayout_kw1.setContentsMargins(34, 2, 24, 2)
        self.hBoxLayout_kw1.addWidget(self.kw1_label, 1)
        self.hBoxLayout_kw1.addWidget(self.kw1_le, 4)
        
        self.hBoxLayout_kw2.setSpacing(30)
        self.hBoxLayout_kw2.setContentsMargins(34, 2, 24, 2)
        self.hBoxLayout_kw2.addWidget(self.kw2_label, 1)
        self.hBoxLayout_kw2.addWidget(self.kw2_le, 4)
        
        self.hBoxLayout_kw3.setSpacing(30)
        self.hBoxLayout_kw3.setContentsMargins(34, 2, 24, 2)
        self.hBoxLayout_kw3.addWidget(self.kw3_label, 1)
        self.hBoxLayout_kw3.addWidget(self.kw3_le, 4)
        
        self.vBoxLayout_switch.setSpacing(30)
        self.vBoxLayout_switch.setContentsMargins(34, 2, 24, 2)
        self.vBoxLayout_switch.addWidget(self.switch_btn)
        self.vBoxLayout_switch.addWidget(self.tips_label)
        
        self.vBoxLayout_chat.setContentsMargins(34, 2, 24, 2)
        self.vBoxLayout_chat.addWidget(self.chat_label)
        
        
        self.vBoxLayout.addLayout(self.vBoxLayout_alert)
        self.vBoxLayout.addLayout(self.vBoxLayout_chat)
        self.vBoxLayout.addLayout(self.vBoxLayout_switch)
        self.vBoxLayout.addLayout(self.hBoxLayout_combo)
        self.vBoxLayout.addLayout(self.hBoxLayout_kw1)
        self.vBoxLayout.addLayout(self.hBoxLayout_kw2)
        self.vBoxLayout.addLayout(self.hBoxLayout_kw3)
        
        
        
    def update_win_combo(self):
        self.win_combo.clear()
        hwnd_title.clear()
        win32gui.EnumWindows(get_all_hwnd, 0)
        for h, t in hwnd_title.items():
            if t.startswith('梦幻西游 聊天窗口'):
                self.win_combo.addItem(str(h)+":"+t)
    
    def UpdateImage(self, img_path, rect):
        
        cv_image = cv2.imread(img_path)
        # 将图像转换为 RGB 格式
        cv_image = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)
        
        height, width, channel = cv_image.shape
        logger.info(f"cv2 {height} {width}")
        # print(f"Height: {height} Width {width}")
        bytesPerLine = 3 * width
        
        if rect[0] > width:
            rect[0] = 0
        if rect[2] > width:
            rect[2] = width
        if rect[1] > height:
            rect[1] = 0
        if rect[3] > height:
            rect[3] = height
        
        # self.img_rect = rect
        logger.info(f"rect {rect}")
        
        top_left = (rect[0], rect[1])
        bottom_right  = (rect[2], rect[3])
        
        cv2.rectangle(cv_image, top_left, bottom_right, (255, 0, 0), 3)
        
        # 将 OpenCV 图像转换为 QImage
        qImage = QImage(cv_image.data, width, height, bytesPerLine, QImage.Format_RGB888)
        
        # 从 QImage 创建 QPixmap
        pixmap = QPixmap.fromImage(qImage)
        
        # pixmap = QPixmap(map_img_path)  
        
        scaled_pixmap = pixmap.scaled(c.win_width-140, c.win_width-140, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        width = scaled_pixmap.width()
        height = scaled_pixmap.height()
        # logger.info(f"w: {width}, h: {height}")
        self.chat_label.setPixmap(scaled_pixmap)
                
    def load_subscribe_kw2sound(self):
        items, ok = loadini_subscribe_kw2sound()
        if not ok:
            logger.warning("loadini_subscribe_kw2sound failed!")
            return False
        else:
            for key, value in items:
                self.kw2sound[key] = value
        logger.info("loadini_subscribe_kw2sound success!")
        print(self.kw2sound)

    def show(self):
        self.update_win_combo()
        
    def setTips(self, tips):
        self.tips_label.setText(f"<font color='red'>{tips}</font>")
        
    def test(self):
        file_img = "D:\workspace\mhxyQA_GUI\images\shot\\chat_yw.jpg"
        
        self.kw[0] = self.kw1_le.text()
        self.kw[1] = self.kw2_le.text()
        self.kw[2] = self.kw3_le.text()
        re, ok = maocr.subscribe_detect(file_img, self.kw[0], self.kw[1], self.kw[2], u.signin_token)
        # delete_file(file_img)
        if ok:
            print(f"re {re}")
            left = int(re[4])
            top = int(re[5])
            right = int(re[6])
            bottom = int(re[7])
            self.UpdateImage(file_img, [left, top, right, bottom])
            idx = re[1]
            if idx != -1:
                logger.info(f"{self.kw[idx]} detected")
                if self.kw[idx] in self.kw2sound:
                    au = os.path.join(c.flag_audio_dir_path, self.kw2sound[self.kw[idx]]) 
                    logger.info(f"spec play {au}")
                else:
                    au = os.path.join(c.flag_audio_dir_path, default_sound[idx])
                    logger.info(f"defult play {au}")
                playsound(au)
        
    def onclick_switch(self):
        
        # self.test()
        # return
        
        if c.URL_QA == "":
            self.setTips("请先设置后台 ip 和 port")
            return
        
        if not self.flag_start and not u.signin_flag:
            msg = "仅登录用户可用"
            self.setTips(msg)
            logger.warning(msg) 
            return
        
        _, ok = self.getWinText()
        if not ok:
            self.flag_start = False  
            self.updateSwitch()
            self.setTips("窗口异常， 自动停止")
            return
        
        self.kw[0] = self.kw1_le.text()
        self.kw[1] = self.kw2_le.text()
        self.kw[2] = self.kw3_le.text()
        self.setTips("")
        if self.kw[0] == "" and self.kw[1] == "" and self.kw[2] == "":
            self.setTips("关键词为空")
            return
            
        self.flag_start = not self.flag_start  
        self.updateSwitch()
        
    def updateSwitch(self):
        if self.flag_start:
            self.setTips("订阅监测中...")
            self.switch_btn.setText(f"停止订阅监测 {self.str_leftcount}")
        else :
            self.setTips("订阅监测已停止")
            self.switch_btn.setText(f"启动订阅监测 {self.str_leftcount}")
            
    def getWinText(self):
        win_text = self.win_combo.currentText()
        if win_text == "":
            msg = "没有可用窗口"
            logger.warning(msg)
            self.setTips(msg) 
            self.update_win_combo()
            return None, False
        h = win_text.split(':')[0]
        t = win_text.split(':')[1]
        logger.info(f"{h} {t}")
        
        if not win32gui.IsWindowEnabled(h):
            self.flag_start = False
            self.updateSwitch()
            self.setTips("无效窗口， 自动停止")
            self.update_win_combo()
            return None, False
    
        return (h, t), True
    
    def detect(self):
        
        ht, ok = self.getWinText()
        if not ok:
            self.flag_start = False 
            self.updateSwitch()
            return
        h = ht[0]
        t = ht[1]
        # img_hwnd = get_screenshot_image(int(h))
        img_hwnd = get_mss_screenshot_image(int(h))
        logger.info(f"shot size {img_hwnd.size}")
        
        ts = time_str()
        file_img = f"{c.shot_dir_path}/{h}_{t}_{ts}.png"
        img_hwnd.save(file_img, 'PNG')
        logger.info(f'img_desktop save to -> {os.path.abspath(file_img)}')
        if not u.signin_flag or u.signin_token == None:
            self.flag_start = False 
            self.updateSwitch()
            self.setTips("登录退出， 自动停止")
            return
            
        re, ok = maocr.subscribe_detect(file_img, self.kw[0], self.kw[1], self.kw[2], u.signin_token)
        delete_file(file_img)
        if ok:
            print(f"re {re}")
            left = int(re[4])
            top = int(re[5])
            right = int(re[6])
            bottom = int(re[7])
            self.UpdateImage(file_img, [left, top, right, bottom])
            idx = re[1]
            if idx != -1:
                logger.info(f"{self.kw[idx]} detected")
                if self.kw[idx] in self.kw2sound:
                    au = os.path.join(c.flag_audio_dir_path, self.kw2sound[self.kw[idx]]) 
                    logger.info(f"spec play {au}")
                else:
                    au = os.path.join(c.flag_audio_dir_path, default_sound[idx])
                    logger.info(f"defult play {au}")
                playsound(au)
            
            self.str_leftcount = str(re[2])
            self.updateSwitch()
            self.setTips(re[3])
            if re[2] <= 0:
                self.flag_start = False
                self.updateSwitch()
                self.setTips("订阅监测次数余额不足， 自动停止")
        else:
            self.flag_start = False 
            self.updateSwitch()
            self.setTips("服务器连接异常， 自动停止")


class SubscribeInterface(SingleDirectionScrollArea):

    def __init__(self, parent=None):
        super().__init__(parent=None)

        self.view = QWidget(self)

        self.vBoxLayout = QVBoxLayout(self.view)
        self.subscribeCard = SubscribeInfoCard(self)
        self.subscribeCard.setMaximumWidth(c.card_width)
        self.setWidget(self.view)
        self.setWidgetResizable(True)
        self.setObjectName('SubscribeInterface')

        self.vBoxLayout.setSpacing(10)
        self.vBoxLayout.setContentsMargins(0, 0, 15, 0)
        self.vBoxLayout.addWidget(self.subscribeCard, 0, Qt.AlignTop)

        self.setStyleSheet("QScrollArea {border: none; background:transparent}")
        self.view.setStyleSheet('QWidget {background:transparent}')
        
    def show(self):
        print("show SubscribeInterface")
        self.subscribeCard.show()